/*********************************************************************************************************
* Porting Mr. DOOB'S Harmony drawing application to MAX MSP JITTER
* http://github.com/mrdoob/harmony
*  
*  Please take care of:
*	This program is free software: you can redistribute it and/or modify
*	it under the terms of the GNU General Public License as published by
*	the Free Software Foundation, either version 3 of the License, or
*	(at your option) any later version.
*
*	This program is distributed in the hope that it will be useful,
*	but WITHOUT ANY WARRANTY; without even the implied warranty of
*	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*	GNU General Public License for more details.
*
*	You should have received a copy of the GNU General Public License
*	along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*  Author of the ported code: Florian Weil - www.derhess.de
*
*
*  Improvements: 
*
*   - Add a drawing and brush manager, so that more user can simultaneously draw on the canvas
************************************************************************************************************/

inlets = 2;
outlets = 1;

// color
var red = 125;
var green = 125;
var blue = 125;
var colorArray = new Array(red,green,blue);

// Init Variables
var BRUSH_SIZE = 0.000002;
var BRUSH_PRESSURE = 1;
var BRUSH_TYPE = "simple";
var BRUSH_MANAGER = new Array();



var activeNameID = "brush1";
BRUSH_MANAGER[activeNameID] = new simple(this,colorArray,BRUSH_SIZE,BRUSH_PRESSURE);

// set up jsui defaults to 2d
sketch.default2d();
// initialize graphics
var outputImg = new Image(sketch);
var outputMatrix = new JitterMatrix(4, "char", sketch.size[0], sketch.size[1]); 

////////////////////////////////////////////////////////////
// Global and API Functions 



function setCoordinate(nameID,xpos,ypos)
{
	if(arguments.length == 3)
	{
	
		if(isBrushNameExisting(nameID))
		{
			activeNameID = nameID;
			if(BRUSH_MANAGER[nameID].startDrawing)
			{
				BRUSH_MANAGER[nameID].strokeStart(xpos,ypos);
				BRUSH_MANAGER[nameID].startDrawing = false;
			}
			else
			{
				BRUSH_MANAGER[nameID].stroke(xpos,ypos);
			}
			// Rendering and Refresh
			draw();
		} 
			else
		{
			post("setCoordinate: Brush ID doesnot exist\n");
		}
		
	}
		else
	{
		post("setCoordinate: To less arguments\n");
	}
}

function stopDrawing(nameID)
{
	if(isBrushNameExisting(nameID))
	{
		// here appears a bug with the ribbon?! Timer has no properties
		if(BRUSH_MANAGER[nameID].type == "ribbon") {
			BRUSH_MANAGER[nameID].destroy();
			BRUSH_MANAGER[nameID].created = true;
		}
				
				
		BRUSH_MANAGER[nameID].clear();
		BRUSH_MANAGER[nameID].startDrawing = true;
	}  else {
		post("stopDrawing: Brush ID doesnot exist\n");
	}
}

function selectBrush(nameID)
{
	activeNameID = nameID;
	// here appears a bug with the ribbon?! Timer has no properties
	
	//if(BRUSH_MANAGER[nameID].type == "ribbon")
		//BRUSH_MANAGER[nameID].start();
}


function setBrush(nameID,type)
{
	if(arguments.length == 2)
	{
		
		if(isBrushNameExisting(nameID))
		{
		
			if(BRUSH_MANAGER[nameID].type == "ribbon")
				BRUSH_MANAGER[nameID].destroy();
			
			BRUSH_MANAGER[nameID].clear();
		} 
		
		switch(type)
		{
			case "simple":
			  BRUSH_MANAGER[nameID] = new simple(this,colorArray,BRUSH_SIZE,BRUSH_PRESSURE);
			  break;
			case "chrome":
			  BRUSH_MANAGER[nameID] = new chrome(this,colorArray,BRUSH_SIZE,BRUSH_PRESSURE);
			  break;
			case "fur":
			  BRUSH_MANAGER[nameID] = new fur(this,colorArray,BRUSH_SIZE,BRUSH_PRESSURE);
			  break;
			case "longFur":
			  BRUSH_MANAGER[nameID] = new longFur(this,colorArray,BRUSH_SIZE,BRUSH_PRESSURE);
			  break;
			case "ribbon":
			  BRUSH_MANAGER[nameID] = new ribbon(this,colorArray,BRUSH_SIZE,BRUSH_PRESSURE);
			  break;
			case "shaded":
			  BRUSH_MANAGER[nameID] = new shaded(this,colorArray,BRUSH_SIZE,BRUSH_PRESSURE);
			  break;
			case "sketchy":
			  BRUSH_MANAGER[nameID] = new sketchy(this,colorArray,BRUSH_SIZE,BRUSH_PRESSURE);
			  break;
			case "web":
			  BRUSH_MANAGER[nameID] = new web(this,colorArray,BRUSH_SIZE,BRUSH_PRESSURE);
			  break;
			case "squares":
			  BRUSH_MANAGER[nameID] = new squares(this,colorArray,BRUSH_SIZE,BRUSH_PRESSURE);
			  break;
			case "roundSquares":
			  BRUSH_MANAGER[nameID] = new roundSquares(this,colorArray,BRUSH_SIZE,BRUSH_PRESSURE);
			  break;
			default:
			  post("Wrong BrushType!!! Only simple,chrome,fur,longfur,ribbon,shaded,sketchy,web,squares,roundSquares\n");
		}
	}
		else
	{
		post("setBrush: To less arguments\n");
	}
}

function setBrushSize(nameID,val)
{
	if(arguments.length == 2)
	{
		if(isBrushNameExisting(nameID))
		{
			BRUSH_MANAGER[nameID].brushSize = val;
			BRUSH_SIZE = val;
		} else {
			post("setBrushSize: Brush ID doesnot exist\n");
		}
	}
		else
	{
		post("setBrushSize: To less arguments\n");
	}
}

function setBrushPressure(nameID,val)
{
	if(arguments.length == 2)
	{
		if(isBrushNameExisting(nameID))
		{
			BRUSH_PRESSURE = val;
			BRUSH_MANAGER[nameID].pressure = val;
		} else {
			post("setBrushPressure: Brush ID doesnot exist\n");
		}
	}
		else
	{
		post("setBrushPressure: To less arguments\n");
	}
}

function setBrushColor(nameID,r,g,b)
{
	if(arguments.length == 4)
	{
		if(isBrushNameExisting(nameID))
		{
			// color
			red = r;
			green = g;
			blue = b;
			colorArray = new Array(red,green,blue);
			BRUSH_MANAGER[nameID].color = colorArray;
			
		} else {
			post("setBrushColor: Brush ID doesnot exist\n");
		}
	}
		else
	{
		post("setBrushColor: To less arguments\n");
	}
}

function clearCanvas()
{
	// clean every Brush
	for(var index in BRUSH_MANAGER) {
		if(BRUSH_MANAGER[index] != null)
		{
			if(BRUSH_MANAGER[index].type == "ribbon")
				BRUSH_MANAGER[index].destroy();
				
			BRUSH_MANAGER[index].clear(); 
		}
	}

	
    deleteCanvas();
	//outputImg.outputImg.copypixels (sketch,0,0,0,0, sketch.size[0], sketch.size[1]);
	prepareOutput();
}

// bang -- draw and refresh display
function bang()
{
	clearCanvas();
}

/////////////////////////////////////////////////////////////
//  Helper Functions

function isBrushNameExisting(name)
{
	if(BRUSH_MANAGER[name] != null)
		return true;
	else
		return false;
}

function getBrush(name)
{
	if(BRUSH_MANGER[name] != null)
		return BRUSH_MANAGER[name];
	else
		return null;
}

// Draw Line
function drawLine(target,xPoint1,yPoint1,xPoint2,yPoint2,r,g,b,a,bSize)
{
	sketch.beginstroke("basic2d");
	sketch.strokeparam ("scale", bSize);
	var tColor = mapColor2RGBA(r,g,b,a);
	sketch.strokeparam ("color",tColor[0],tColor[1],tColor[2],tColor[3]);
	
	var old_x = sketch.screentoworld(xPoint1, yPoint1)[0];
	var old_y = sketch.screentoworld(xPoint1, yPoint1)[1];
	var new_x = sketch.screentoworld(xPoint2, yPoint2)[0];
	var new_y = sketch.screentoworld(xPoint2, yPoint2)[1];
	
	sketch.strokepoint(old_x,old_y);
	sketch.strokepoint(new_x,new_y);
	//sketch.linesegment (old_x, old_y, 0, new_x, new_y, 0);
	sketch.endstroke();
}
// map the color to OpenGL Color
function mapColor2RGBA(r,g,b,a)
{
	var rgbaArray = new Array();
	rgbaArray[0] = r/255.;
	rgbaArray[1] = g/255.;
	rgbaArray[2] = b/255.;
	rgbaArray[3] = a/255.;
	return rgbaArray;
}

function prepareOutput()
{
	// Generate output
	//outputImg = new Image(sketch);  --> Version 1
	outputImg.copypixels(sketch,0,0,0,0, sketch.size[0], sketch.size[1]);

	outputImg.tonamedmatrix(outputMatrix.name);
	outlet(0, "jit_matrix", outputMatrix.name);
}

////////////////////////////////////////////////////////////
// Rendering

function draw()
{
	refresh();
	prepareOutput();
}

function deleteCanvas()
{
	sketch.glclear();
    refresh();
}

////////////////////////////////////////////////////////////
// Interaction
function onclick(x,y)
{
	BRUSH_MANAGER[activeNameID].strokeStart(x,y);
	startDrawing = false;
}
onclick.local = 1; // make function private to prevent triggering from Max

function ondrag(x,y)
{
	BRUSH_MANAGER[activeNameID].stroke(x,y);
	draw();
}

ondrag.local = 1;


///////////////////////////////////////////////////////////////////////////
//////////////////// Brushes ////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

///////// SIMPLE BRUSH ////////////////
function simple( context,color, lineSize, press )
{
	this.context = context;
	this.pressure = press;
	this.prevMouseX = null;
	this.prevMouseY = null;
	this.color = color;
	this.brushSize = lineSize;
	this.type = "simple";
	this.startDrawing = true;
	//this.init( context );
}

function simpleStrokeStart( mouseX, mouseY )
{
	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;
}
simple.prototype.strokeStart = simpleStrokeStart;

function simpleStroke( mouseX, mouseY )
{
	drawLine(this.context,
			this.prevMouseX,this.prevMouseY,
			mouseX,mouseY,
			this.color[0],this.color[1],this.color[2],255,
			this.brushSize);

	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;
}
simple.prototype.stroke = simpleStroke;

function simpleClear()
{
	this.prevMouseX = null;
	this.prevMouseY = null;
	this.brushSize = BRUSH_SIZE;
	this.startDrawing = false;
	//this.init( context );
}
simple.prototype.clear = simpleClear;



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////// CHROME ////////////////
function chrome( context,color, lineSize, press )
{
	this.context = context;
	this.type = "chrome";
	this.pressure = press;
	this.prevMouseX = null;
	this.prevMouseY = null;
	this.color = color;
	this.brushSize = lineSize;
	//this.brushSize = 0.0000002;

	this.points = new Array();
	this.count = 0;
	this.startDrawing = true;
}


function chromeStrokeStart( mouseX, mouseY )
{
	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;
}
chrome.prototype.strokeStart = chromeStrokeStart;

function chromeStroke( mouseX, mouseY )
{
	var i, dx, dy, d;
	
	this.points.push( [ mouseX, mouseY ] );
	
	drawLine(this.context,
			this.prevMouseX,this.prevMouseY,
			mouseX,mouseY,
			this.color[0],this.color[1],this.color[2],255 * 0.1 * this.pressure,	
			//this.color[0],this.color[1],this.color[2],255,
			this.brushSize);
	
	for (i = 0; i < this.points.length; i++)
	{
		dx = this.points[i][0] - this.points[this.count][0];
		dy = this.points[i][1] - this.points[this.count][1];
		d = dx * dx + dy * dy;

		if (d < 1000)
		{
			drawLine(this.context,
					this.points[this.count][0] + (dx * 0.2),this.points[this.count][1] + (dy * 0.2),
					this.points[i][0] - (dx * 0.2), this.points[i][1] - (dy * 0.2),
					Math.floor(Math.random() * this.color[0]), Math.floor(Math.random() * this.color[1]), Math.floor(Math.random() * this.color[2]),255 * 0.1 * this.pressure,
					//this.color[0],this.color[1],this.color[2],255,
					this.brushSize);
		}
	}

	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;

	this.count ++;
}
chrome.prototype.stroke = chromeStroke;

function chromeClear()
{
	this.prevMouseX = null;
	this.prevMouseY = null;
	//this.brushSize = lineSize;
	this.brushSize = 0.0000002;
	this.startDrawing = true;
	this.points = new Array();
	this.count = 0;
}
chrome.prototype.clear = chromeClear;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////// FUR ////////////////
function fur( context,color, lineSize, press )
{
	this.context = context;
	this.type = "fur";
	this.pressure = press;
	this.prevMouseX = null;
	this.prevMouseY = null;
	this.color = color;
	this.brushSize = lineSize;
	//this.brushSize = 0.000002; // thin
	//this.brushSize = 0.002;  // bigger
	//this.brushSize = 0.02; // quite big
	this.startDrawing = true;
	this.points = new Array();
	this.count = 0;
}



function furStrokeStart( mouseX, mouseY )
{
	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;
}
fur.prototype.strokeStart = furStrokeStart;

function furStroke( mouseX, mouseY )
{
	var i, dx, dy, d;
	
	this.points.push( [ mouseX, mouseY ] );
	
	drawLine(this.context,
			this.prevMouseX,this.prevMouseY,
			mouseX,mouseY,
			this.color[0],this.color[1],this.color[2],255 * 0.1 * this.pressure,
			//this.color[0],this.color[1],this.color[2],255,
			this.brushSize);

	for (i = 0; i < this.points.length; i++)
	{
		dx = this.points[i][0] - this.points[this.count][0];
		dy = this.points[i][1] - this.points[this.count][1];
		d = dx * dx + dy * dy;

		if (d < 2000 && Math.random() > d / 2000)
		{
			drawLine(this.context,
					mouseX + (dx * 0.5), mouseY + (dy * 0.5),
					mouseX - (dx * 0.5), mouseY - (dy * 0.5),
					this.color[0],this.color[1],this.color[2],255 * 0.1 * this.pressure,
					//this.color[0],this.color[1],this.color[2],255,
					this.brushSize);
		}
	}

	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;

	this.count ++;
}
fur.prototype.stroke = furStroke;

function furClear()
{
	this.prevMouseX = null;
	this.prevMouseY = null;
	//this.brushSize = lineSize;
	this.brushSize = 0.0000002;
	this.startDrawing = true;
	this.points = new Array();
	this.count = 0;
}
fur.prototype.clear = furClear;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////// LONG FUR ////////////////
function longFur( context,color, lineSize, press )
{
	this.context = context;
	this.type = "longFur";
	this.pressure = press;
	this.color = color;
	this.brushSize = lineSize;
	//this.brushSize = 0.000002; // thin
	//this.brushSize = 0.002;  // bigger
	//this.brushSize = 0.02; // quite big
	this.startDrawing = true;
	this.points = new Array();
	this.count = 0;
}

function longFurStrokeStart( mouseX, mouseY )
{
}
longFur.prototype.strokeStart = longFurStrokeStart;

function longFurStroke( mouseX, mouseY )
{
	var i, size, dx, dy, d;

	this.points.push( [ mouseX, mouseY ] );

	for (i = 0; i < this.points.length; i++)
	{
		size = -Math.random();
		dx = this.points[i][0] - this.points[this.count][0];
		dy = this.points[i][1] - this.points[this.count][1];
		d = dx * dx + dy * dy;

		if (d < 4000 && Math.random() > d / 4000)
		{
			drawLine(this.context,
					this.points[this.count][0] + (dx * size), this.points[this.count][1] + (dy * size),
					this.points[i][0] - (dx * size) + Math.random() * 2, this.points[i][1] - (dy * size) + Math.random() * 2,
					this.color[0],this.color[1],this.color[2],255 * 0.05 * this.pressure,
					//this.color[0],this.color[1],this.color[2],255,
					this.brushSize);
		}
	}
	
	this.count++;
}
longFur.prototype.stroke = longFurStroke;

function longFurClear()
{
	this.brushSize = 0.0000002;

	this.points = new Array();
	this.count = 0;
	this.startDrawing = true;
}
longFur.prototype.clear = longFurClear;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////// SHADED ////////////////
function shaded( context,color, lineSize, press)
{
	this.context = context;
	this.type = "shaded";
	this.pressure = press;
	this.prevMouseX = null;
	this.prevMouseY = null;
	this.color = color;
	this.brushSize = lineSize;
	//this.brushSize = 0.000002; // thin
	//this.brushSize = 0.002;  // bigger
	//this.brushSize = 0.02; // quite big
	this.startDrawing = true;
	this.points = new Array();
	this.count = 0;
}

function shadedStrokeStart( mouseX, mouseY )
{
	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;
}
shaded.prototype.strokeStart = shadedStrokeStart;

function shadedStroke( mouseX, mouseY )
{
	var i, dx, dy, d;

	this.points.push( [ mouseX, mouseY ] );

	for (i = 0; i < this.points.length; i++)
	{
		dx = this.points[i][0] - this.points[this.count][0];
		dy = this.points[i][1] - this.points[this.count][1];
		d = dx * dx + dy * dy;

		if (d < 1000)
		{
			drawLine(this.context,
					this.points[this.count][0], this.points[this.count][1],
					this.points[i][0], this.points[i][1],
					this.color[0],this.color[1],this.color[2],255 * 0.1 * (1 - (d / 1000)) * this.pressure,
					//this.color[0],this.color[1],this.color[2],255,
					this.brushSize);
		}
	}

	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;

	this.count ++;
}
shaded.prototype.stroke = shadedStroke;

function shadedClear()
{
	this.prevMouseX = null;
	this.prevMouseY = null;
	//this.brushSize = lineSize;
	this.brushSize = 0.0000002;
	this.startDrawing = true;
	this.points = new Array();
	this.count = 0;
}
shaded.prototype.clear = shadedClear;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////// SKETCHY ////////////////
function sketchy( context,color, lineSize, press)
{
	this.context = context;
	this.type = "sketchy";
	this.pressure = press;
	this.prevMouseX = null;
	this.prevMouseY = null;
	this.color = color;
	this.brushSize = lineSize;
	//this.brushSize = 0.000002; // thin
	//this.brushSize = 0.002;  // bigger
	//this.brushSize = 0.02; // quite big
	this.startDrawing = true;
	this.points = new Array();
	this.count = 0;
}

function sketchyStrokeStart( mouseX, mouseY )
{
	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;
}
sketchy.prototype.strokeStart = sketchyStrokeStart;

function sketchyStroke( mouseX, mouseY )
{
	var i, dx, dy, d;

	this.points.push( [ mouseX, mouseY ] );

	drawLine(this.context,
			this.prevMouseX, this.prevMouseY,
			mouseX, mouseY,
			this.color[0],this.color[1],this.color[2],255 * 0.05 * this.pressure,
			//this.color[0],this.color[1],this.color[2],255,
			this.brushSize);

	for (i = 0; i < this.points.length; i++)
	{
		dx = this.points[i][0] - this.points[this.count][0];
		dy = this.points[i][1] - this.points[this.count][1];
		d = dx * dx + dy * dy;

		if (d < 4000 && Math.random() > (d / 2000))
		{
			drawLine(this.context,
					this.points[this.count][0] + (dx * 0.3), this.points[this.count][1] + (dy * 0.3),
					this.points[i][0] - (dx * 0.3), this.points[i][1] - (dy * 0.3),
					this.color[0],this.color[1],this.color[2],255 * 0.05 * this.pressure,
					//this.color[0],this.color[1],this.color[2],255,
					this.brushSize);
		}
	}

	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;

	this.count ++;
}
sketchy.prototype.stroke = sketchyStroke;

function sketchyClear()
{
	this.prevMouseX = null;
	this.prevMouseY = null;
	//this.brushSize = lineSize;
	this.brushSize = 0.0000002;
	this.startDrawing = true;
	this.points = new Array();
	this.count = 0;
}
sketchy.prototype.clear = sketchyClear;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////// WEB ////////////////
function web( context,color, lineSize, press )
{
	this.context = context;
	this.type = "web";
	this.pressure = press;
	this.prevMouseX = null;
	this.prevMouseY = null;
	this.color = color;
	this.brushSize = lineSize;
	//this.brushSize = 0.000002; // thin
	//this.brushSize = 0.002;  // bigger
	//this.brushSize = 0.02; // quite big
	this.startDrawing = true;
	this.points = new Array();
	this.count = 0;
}

function webStrokeStart( mouseX, mouseY )
{
	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;
}
web.prototype.strokeStart = webStrokeStart;

function webStroke( mouseX, mouseY )
{
	var i, dx, dy, d;

	this.points.push( [ mouseX, mouseY ] );

	// Tip: maybe comment this first drawing code for a more realistic sketch based style!!
	drawLine(this.context,
			this.prevMouseX, this.prevMouseY,
			mouseX, mouseY,
			this.color[0],this.color[1],this.color[2],255 * 0.5 * this.pressure,
			//this.color[0],this.color[1],this.color[2],255,
			this.brushSize);

	for (i = 0; i < this.points.length; i++)
	{
		dx = this.points[i][0] - this.points[this.count][0];
		dy = this.points[i][1] - this.points[this.count][1];
		d = dx * dx + dy * dy;

		 if (d < 2500 && Math.random() > 0.9)
		{
			drawLine(this.context,
					this.points[this.count][0], this.points[this.count][1],
					this.points[i][0], this.points[i][1],
					this.color[0],this.color[1],this.color[2],255 * 0.05 * this.pressure,
					//this.color[0],this.color[1],this.color[2],255,
					this.brushSize);
		}
	}

	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;

	this.count ++;
}
web.prototype.stroke = webStroke;

function webClear()
{
	this.prevMouseX = null;
	this.prevMouseY = null;
	//this.brushSize = lineSize;
	this.brushSize = 0.0000002;
	this.startDrawing = true;
	this.points = new Array();
	this.count = 0;
}
web.prototype.clear = webClear;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////// RIBBON ////////////////
function ribbon(context,color, lineSize,press)
{
	this.context = context;
	this.type = "ribbon";
	this.pressure = press;
	this.canvasWidth = this.context.sketch.size[0];
	this.canvasHeight = this.context.sketch.size[1];
	this.color = color;
	this.brushSize = lineSize;
	this.startDrawing = true;
	this.created = true;
	//this.brushSize = 0.000002; // thin
	//this.brushSize = 0.002;  // bigger
	//this.brushSize = 0.02; // quite big
}



function ribbonStart()
{
	var scope = this;

	this.mouseX = this.canvasWidth / 2;
	this.mouseY = this.canvasHeight / 2;

	this.painters = new Array();
	
	for (var i = 0; i < 50; i++)
	{
		this.painters.push({ dx: this.canvasWidth / 2, dy: this.canvasHeight / 2, ax: 0, ay: 0, div: 0.1, ease: Math.random() * 0.2 + 0.6 });
	}
	
	this.timer = new Task( update,scope)
	this.timer.interval =  1000/60;
	
	function update()
	{
		var i;
		var scope = arguments.callee.task.object;
		//post("Ribbon updating Function is running");
		for (i = 0; i < scope.painters.length; i++)
		{
			// much cleaner version but it is not drawing, Don'T know why
			/*var r_old_x = sketch.screentoworld(scope.painters[i].dx, scope.painters[i].dy)[0];
			var r_old_y = sketch.screentoworld(scope.painters[i].dx, scope.painters[i].dy)[1];
			
			scope.painters[i].dx -= scope.painters[i].ax = (scope.painters[i].ax + (scope.painters[i].dx - scope.mouseX) * scope.painters[i].div) * scope.painters[i].ease;
			scope.painters[i].dy -= scope.painters[i].ay = (scope.painters[i].ay + (scope.painters[i].dy - scope.mouseY) * scope.painters[i].div) * scope.painters[i].ease;
			
			var r_new_x = sketch.screentoworld(scope.painters[i].dx, scope.painters[i].dy)[0];
			var r_new_y = sketch.screentoworld(scope.painters[i].dx, scope.painters[i].dy)[1];
			
			drawLine(scope.context,
					r_old_x,r_old_y,
					r_new_x,r_new_y,
					scope.color[0],scope.color[1],scope.color[2],255 * 0.05 * scope.pressure,
					//scope.color[0],scope.color[1],scope.color[2],255,
					scope.brushSize);
			post("Ribbon Draw :: ("+ r_old_x +"/"+ r_old_y + ")   ::::   (" + r_new_x + "/" + r_new_y + ")\n");
			post("Ribbon Color: " + scope.color[0] + " / " + scope.color[1] + " / " + scope.color[2] + " / " +255 * 0.05 * scope.pressure + "\n");
			post("Ribbon Brush Size:" + scope.brushSize + " --- " + scope.pressure + "\n");*/
			
			/*** not so nice code solution, but it works without problems **/
			scope.context.sketch.beginstroke("basic2d");
			scope.context.sketch.strokeparam ("scale", scope.brushSize);
			var tColor = mapColor2RGBA(scope.color[0],scope.color[1],scope.color[2],255 * 0.05 * scope.pressure);
			this.context.sketch.strokeparam ("color",tColor[0],tColor[1],tColor[2],tColor[3]);
			
			old_x = sketch.screentoworld(scope.painters[i].dx, scope.painters[i].dy)[0];
			old_y = sketch.screentoworld(scope.painters[i].dx, scope.painters[i].dy)[1];
			
			scope.painters[i].dx -= scope.painters[i].ax = (scope.painters[i].ax + (scope.painters[i].dx - scope.mouseX) * scope.painters[i].div) * scope.painters[i].ease;
			scope.painters[i].dy -= scope.painters[i].ay = (scope.painters[i].ay + (scope.painters[i].dy - scope.mouseY) * scope.painters[i].div) * scope.painters[i].ease;
			
			new_x = sketch.screentoworld(scope.painters[i].dx, scope.painters[i].dy)[0];
			new_y = sketch.screentoworld(scope.painters[i].dx, scope.painters[i].dy)[1];

			this.context.sketch.strokepoint(old_x,old_y);
			this.context.sketch.strokepoint(new_x,new_y);
			//sketch.linesegment (old_x, old_y, 0, new_x, new_y, 0);
			this.context.sketch.endstroke();
		}
	}
	
	this.timer.repeat(-1); // repeat it until it is canceled
}
ribbon.prototype.start = ribbonStart;
	
function ribbonDestroy()
{
	if(this.timer != null)
		this.timer.cancel();
}
ribbon.prototype.destroy = ribbonDestroy;


function ribbonStrokeStart( mouseX, mouseY )
{
	if(this.created)
	{
		this.start();
		this.created = false;
	}

	this.mouseX = mouseX;
	this.mouseY = mouseY

	for (var i = 0; i < this.painters.length; i++)
	{
		this.painters[i].dx = mouseX;
		this.painters[i].dy = mouseY;
	}

	this.shouldDraw = true;
}
ribbon.prototype.strokeStart = ribbonStrokeStart;

function ribbonStroke( mouseX, mouseY )
{
	this.mouseX = mouseX;
	this.mouseY = mouseY;
}
ribbon.prototype.stroke = ribbonStroke;


function ribbonClear()
{
	this.canvasWidth = this.context.sketch.size[0];
	this.canvasHeight = this.context.sketch.size[1];
	
	//this.brushSize = lineSize;
	this.brushSize = 0.000002; // thin
	//this.brushSize = 0.002;  // bigger
	//this.brushSize = 0.02; // quite big

	this.mouseX = null;
	this.mouseY = null;
	this.startDrawing = true;
	this.painters = null;
	this.timer = null;
	this.created = true;
}
ribbon.prototype.clear = ribbonClear;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////// SQUARES ////////////////
function squares( context,color, lineSize, press )
{
	this.context = context;
	this.type = "squares";
	this.pressure = press;
	this.prevMouseX = null;
	this.prevMouseY = null;
	this.color = color;
	this.brushSize = lineSize;
	//this.brushSize = 0.000002; // thin
	//this.brushSize = 0.002;  // bigger
	//this.brushSize = 0.02; // quite big
	this.startDrawing = true;
	
}

function squaresStrokeStart( mouseX, mouseY )
{
	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;
}
squares.prototype.strokeStart = squaresStrokeStart;

function squaresStroke( mouseX, mouseY )
{
	var dx, dy, angle, px, py;

	dx = mouseX - this.prevMouseX;
	dy = mouseY - this.prevMouseY;
	angle = 1.57079633;
	px = Math.cos(angle) * dx - Math.sin(angle) * dy;
	py = Math.sin(angle) * dx + Math.cos(angle) * dy;

	
	this.context.sketch.beginstroke("basic2d");
	//this.context.sketch.strokeparam ("scale", this.brushSize);
	var tColor = mapColor2RGBA(this.color[0],this.color[1],this.color[2],255 * this.pressure);
	this.context.sketch.strokeparam ("color",tColor[0],tColor[1],tColor[2],tColor[3]);
	
	var point1_x = sketch.screentoworld(this.prevMouseX - px, this.prevMouseY - py)[0];
	var point1_y = sketch.screentoworld(this.prevMouseX - px, this.prevMouseY - py)[1];
	var point2_x = sketch.screentoworld(this.prevMouseX + px, this.prevMouseY + py)[0];
	var point2_y = sketch.screentoworld(this.prevMouseX + px, this.prevMouseY + py)[1];
	var point3_x = sketch.screentoworld(mouseX + px, mouseY + py)[0];
	var point3_y = sketch.screentoworld(mouseX + px, mouseY + py)[1];
	var point4_x = sketch.screentoworld(mouseX - px, mouseY - py)[0];
	var point4_y = sketch.screentoworld(mouseX - px, mouseY - py)[1];
	sketch.framequad (point1_x,point1_y,1, point2_x, point2_y, 1, point3_x, point3_y, 1, point4_x, point4_y, 1);
	//this.context.sketch.endstroke();

	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;
}
squares.prototype.stroke = squaresStroke;

function squaresClear()
{
	this.prevMouseX = null;
	this.prevMouseY = null;
	//this.brushSize = lineSize;
	this.brushSize = 0.0000002;
	this.startDrawing = true;
}
squares.prototype.clear = squaresClear;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////// Round SQUARES ////////////////
function roundSquares( context,color, lineSize, press )
{
	this.context = context;
	this.type = "roundSquares";
	this.pressure = press;
	this.prevMouseX = null;
	this.prevMouseY = null;
	this.color = color;
	this.brushSize = lineSize;
	//this.brushSize = 0.000002; // thin
	//this.brushSize = 0.002;  // bigger
	//this.brushSize = 0.02; // quite big
	this.startDrawing = true;
	
}

function roundSquaresStrokeStart( mouseX, mouseY )
{
	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;
}
roundSquares.prototype.strokeStart = roundSquaresStrokeStart;

function roundSquaresStroke( mouseX, mouseY )
{
	var dx, dy, angle, px, py;

	dx = mouseX - this.prevMouseX;
	dy = mouseY - this.prevMouseY;
	angle = 1.57079633;
	px = Math.cos(angle) * dx - Math.sin(angle) * dy;
	py = Math.sin(angle) * dx + Math.cos(angle) * dy;

	
	this.context.sketch.beginstroke("basic2d");
	this.context.sketch.strokeparam ("scale", this.brushSize);
	var tColor = mapColor2RGBA(this.color[0],this.color[1],this.color[2],255 * this.pressure);
	this.context.sketch.strokeparam ("color",tColor[0],tColor[1],tColor[2],tColor[3]);
	
	var point1_x = sketch.screentoworld(this.prevMouseX - px, this.prevMouseY - py)[0];
	var point1_y = sketch.screentoworld(this.prevMouseX - px, this.prevMouseY - py)[1];
	var point2_x = sketch.screentoworld(this.prevMouseX + px, this.prevMouseY + py)[0];
	var point2_y = sketch.screentoworld(this.prevMouseX + px, this.prevMouseY + py)[1];
	var point3_x = sketch.screentoworld(mouseX + px, mouseY + py)[0];
	var point3_y = sketch.screentoworld(mouseX + px, mouseY + py)[1];
	var point4_x = sketch.screentoworld(mouseX - px, mouseY - py)[0];
	var point4_y = sketch.screentoworld(mouseX - px, mouseY - py)[1];

	this.context.sketch.strokepoint(point1_x,point1_y);
	this.context.sketch.strokepoint(point2_x,point2_y);
	this.context.sketch.strokepoint(point3_x,point3_y);
	this.context.sketch.strokepoint(point4_x,point4_y);
	this.context.sketch.strokepoint(point1_x,point1_y);

	this.context.sketch.endstroke();

	this.prevMouseX = mouseX;
	this.prevMouseY = mouseY;
}
roundSquares.prototype.stroke = roundSquaresStroke;

function roundSquaresClear()
{
	this.prevMouseX = null;
	this.prevMouseY = null;
	//this.brushSize = lineSize;
	this.brushSize = 0.0000002;
	this.startDrawing = true;
}
roundSquares.prototype.clear = roundSquaresClear;





	



